package com.yfs.cloud.aop;

import java.util.Objects;

import javax.servlet.http.HttpServletRequest;

import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import com.yfs.cloud.annotation.ApiIdempotent;
import com.yfs.cloud.commons.BusinessException;
import com.yfs.cloud.commons.Constants;
import com.yfs.cloud.commons.ResultCode;
import com.yfs.cloud.util.ApiIdempotentTokenUtil;

/**
* @Company: 影法師工作室
* @Description: 接口幂等切面 
* @author 影法師  
* @date 2020年8月24日 下午4:11:46 
* @version V1.0
 */
@Aspect
@Component
public class ApiIdempotentAspect {

	private Logger logger = LoggerFactory.getLogger(this.getClass());

	@Autowired
	private ApiIdempotentTokenUtil adIdempotentTokenUtil;

	@Pointcut("execution(public * com.yfs.cloud.controller.*.*(..))")
	public void donothing() {
	}

	/**
	 * @param
	 * @return void
	 * @author 影法師
	 * @throws Throwable
	 * @description 环绕通知, 幂等,
	 *              可以用拦截器,但是拦截器显然没有aop灵活(aop只需要通过一行注解就可以控制哪个方法要做这些事情,哪个方法不需要做这些事情)
	 * @date 2018/9/15 9:30
	 **/
	@Around("donothing()")
	public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
		// 获取方法签名
		MethodSignature methodSignature = (MethodSignature) proceedingJoinPoint.getSignature();
		// 取得ApiIdempotent注解
		ApiIdempotent apiIdempotent = methodSignature.getMethod().getDeclaredAnnotation(ApiIdempotent.class);
		// 如果此方法上的确加了ApiIdempotent注解的话
		if (!Objects.isNull(apiIdempotent)) {
			String token = getRequest().getHeader(Constants.APIIDEMPOTENT_TOKEN);
			if (StringUtils.isEmpty(token)) {
				logger.info("缺失接口幂等token, 请求被拒绝...");
				throw new BusinessException(ResultCode.APIIDEMPOTENT_TOKEN_EMPTY);
			}
			// 没找到, 表明是重复提交
			if (!adIdempotentTokenUtil.findToken(token)) {
				logger.info("重复调用接口");
				throw new BusinessException(ResultCode.APIIDEMPOTENT_REPEAT_COMMIT);
			}
		}
		// 放行
		return proceedingJoinPoint.proceed();
	}

	/**
	 * @param
	 * @return javax.servlet.http.HttpServletRequest
	 * @author 影法師
	 * @description 取得request
	 * @date 2018/9/15 9:34
	 **/
	private HttpServletRequest getRequest() {
		return ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
	}

}
